#include <sstream>

#include "lsb/rand_embedding_engine.h"

const std::string CRandEmbeddingEngine::sIniSectionName("RAND_LSB");

CRandEmbeddingEngine::CRandEmbeddingEngine(IBitSource* pBitSource) :
	CLSBEmbeddingEngine(NULL),
	mRandom(NULL),
	mSeed(0),
	mMultiplier(0),
	mIncrement(0),
	mModulo(0)
{
	mEmbeddingEngine = new CLSBEmbeddingEngine(pBitSource);
}

CRandEmbeddingEngine::~CRandEmbeddingEngine()
{
	delete mEmbeddingEngine;
	delete mRandom;
}

std::string CRandEmbeddingEngine::getIniSectionName() const
{
	return CRandEmbeddingEngine::sIniSectionName;
}

int CRandEmbeddingEngine::embedIntoSamples(int8_t* pSamples, unsigned int pCount, unsigned int pLeak)
{
	if (sampleToProcess()) { // true means "embed into this sample"
		return mEmbeddingEngine->embedIntoSamples(pSamples, pCount, pLeak);
	}
	// otherwise, we just do nothing on the current sample

	return 1;
}

int CRandEmbeddingEngine::embedIntoSamples(uint8_t* pSamples, unsigned int pCount, unsigned int pLeak)
{
	if (sampleToProcess()) {
		return mEmbeddingEngine->embedIntoSamples(pSamples, pCount, pLeak);
	}

	return 1;
}

int CRandEmbeddingEngine::embedIntoSamples(int16_t* pSamples, unsigned int pCount, unsigned int pLeak)
{
	if (sampleToProcess()) {
		return mEmbeddingEngine->embedIntoSamples(pSamples, pCount, pLeak);
	}

	return 1;
}

int CRandEmbeddingEngine::embedIntoSamples(uint16_t* pSamples, unsigned int pCount, unsigned int pLeak)
{
	if (sampleToProcess()) {
		return mEmbeddingEngine->embedIntoSamples(pSamples, pCount, pLeak);
	}

	return 1;
}

int CRandEmbeddingEngine::extractFromSamples(int8_t* pSamples, unsigned int pCount, unsigned int pLeak, unsigned int pInBuffer)
{
	if (sampleToProcess()) { // true means "extract from this sample"
		return mEmbeddingEngine->extractFromSamples(pSamples, pCount, pLeak, pInBuffer);
	}

	return 0;
}

int CRandEmbeddingEngine::extractFromSamples(uint8_t* pSamples, unsigned int pCount, unsigned int pLeak, unsigned int pInBuffer)
{
	if (sampleToProcess()) {
		return mEmbeddingEngine->extractFromSamples(pSamples, pCount, pLeak, pInBuffer);
	}

	return 0;
}

int CRandEmbeddingEngine::extractFromSamples(int16_t* pSamples, unsigned int pCount, unsigned int pLeak, unsigned int pInBuffer)
{
	if (sampleToProcess()) {
		return mEmbeddingEngine->extractFromSamples(pSamples, pCount, pLeak, pInBuffer);
	}

	return 0;
}

int CRandEmbeddingEngine::extractFromSamples(uint16_t* pSamples, unsigned int pCount, unsigned int pLeak, unsigned int pInBuffer)
{
	if (sampleToProcess()) {
		return mEmbeddingEngine->extractFromSamples(pSamples, pCount, pLeak, pInBuffer);
	}

	return 0;
}

int CRandEmbeddingEngine::changeSettings()
{
	if (getPropertiesReader() == NULL) {
		return -1;
	}

	setProperties();
	initPrng();
	return 0;
}

bool CRandEmbeddingEngine::setProperties()
{
	unsigned int lUInt;
	std::string lString;

	if (getPropertiesReader()->containsKey(sIniSectionName, "SEED")) {
		if (getPropertiesReader()->getValue(sIniSectionName, "SEED", lString)) {
			std::stringstream lStringStream(lString);
			lStringStream >> lUInt;
			if (lStringStream.fail()) {
				return false;
			}

			mSeed = lUInt;
		}
	}

	if (getPropertiesReader()->containsKey(sIniSectionName, "MULTIPLIER")) {
		if (getPropertiesReader()->getValue(
					sIniSectionName, "MULTIPLIER", lString)) {
			std::stringstream lStringStream(lString);
			lStringStream >> lUInt;
			if (lStringStream.fail()) {
				return false;
			}

			mMultiplier = lUInt;
		}
	}

	if (getPropertiesReader()->containsKey(sIniSectionName, "INCREMENT")) {
		if (getPropertiesReader()->getValue(
					sIniSectionName, "INCREMENT", lString)) {
			std::stringstream lStringStream(lString);
			lStringStream >> lUInt;
			if (lStringStream.fail()) {
				return false;
			}

			mIncrement = lUInt;
		}
	}

	if (getPropertiesReader()->containsKey(sIniSectionName, "MODULO")) {
		if (getPropertiesReader()->getValue(
					sIniSectionName, "MODULO", lString)) {
			std::stringstream lStringStream(lString);
			lStringStream >> lUInt;
			if (lStringStream.fail()) {
				return false;
			}

			mModulo = lUInt;
		}
	}

	return true;
}

void CRandEmbeddingEngine::initPrng()
{
	// initialize prng with our values
	delete mRandom;

	// sanity check, if something wasn't initialized (0 is no legal value), than
	// the default values from CLcg are taken
	if (mSeed == 0 || mMultiplier == 0 || mIncrement == 0 || mModulo == 0) {
		mRandom = new CLcg();
	} else {
		mRandom = new CLcg(mSeed, mMultiplier, mIncrement, mModulo);
	}
}

bool CRandEmbeddingEngine::sampleToProcess()
{
	if (mRandom == NULL) {
		initPrng();
	}

	return (mRandom->getRand() % 2) ? true : false;
}

